package com.itstyle.seckill.distributedlock.redis;

import freemarker.template.utility.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁帮助类
 * @author 科帮网 By https://blog.52itstyle.com
 */
public class RedissLockUtil {
    private static RedissonClient redissonClient;

    public void setRedissonClient(RedissonClient locker) {
    	redissonClient = locker;
    }

    public static void main(String[] args) throws InterruptedException {

        Config config = new Config();
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress("redis://127.0.0.1:6379")
                .setTimeout(3000)
                .setConnectionPoolSize(64)
                .setConnectionMinimumIdleSize(10);

        RedissLockUtil redissLockUtil = new RedissLockUtil();
        redissLockUtil.setRedissonClient(Redisson.create(config));
            Runnable r = () ->{
                boolean b = tryLock("test-test", 3, 12);//设置了超时时间，不续期，到了12s就释放锁
                if(b){
                    System.out.println(Thread.currentThread().getName()+"获取锁成功");
                    System.out.println(DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
                    for(int i =0;i<16;i++){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }else{
                    System.out.println(Thread.currentThread().getName()+"获取锁失败");
                    System.out.println(DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
                }
            };
            Runnable r2 = () ->{
                lock("test-test2");//未设置超时时间，每隔10秒自动续期
                System.out.println(Thread.currentThread().getName()+"获取锁成功");
                System.out.println(DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
                for(int i =0;i<16;i++){
                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName()+"--"+DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                unlock("test-test2");
            };
            new Thread(r2).start();
            Thread.sleep(12000);
            new Thread(r2).start();
    }
    /**
     * 加锁（由于每设置超时时间，会每隔10秒续期）
     * @param lockKey
     * @return
     */
    public static RLock lock(String lockKey) {
    	RLock lock = redissonClient.getLock(lockKey);
    	lock.lock();
        return lock;
    }

    /**
     * 释放锁
     * @param lockKey
     */
    public static void unlock(String lockKey) {
    	RLock lock = redissonClient.getLock(lockKey);
		lock.unlock();
    }

    /**
     * 释放锁
     * @param lock
     */
    public static void unlock(RLock lock) {
    	lock.unlock();
    }

    /**
     * 带超时的锁
     * @param lockKey
     * @param timeout 超时时间   单位：秒
     */
    public static RLock lock(String lockKey, int timeout) {
    	RLock lock = redissonClient.getLock(lockKey);
		lock.lock(timeout, TimeUnit.SECONDS);
		return lock;
    }

    /**
     * 带超时的锁（lock拿不到锁会一直等待。tryLock是去尝试，拿不到就返回false，拿到返回true。)
     * @param lockKey
     * @param unit 时间单位
     * @param timeout 超时时间
     */
    public static RLock lock(String lockKey, TimeUnit unit ,int timeout) {
    	RLock lock = redissonClient.getLock(lockKey);
		lock.lock(timeout, unit);
		return lock;
    }

    /**
     * 尝试获取锁
     * @param lockKey
     * @param waitTime 最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
		try {
			return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			return false;
		}
    }

    /**
     * 尝试获取锁(tryLock是可以被打断的，被中断的，lock是不可以。)
     * @param lockKey
     * @param unit 时间单位
     * @param waitTime 最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
    	RLock lock = redissonClient.getLock(lockKey);
		try {
			return lock.tryLock(waitTime, leaseTime, unit);
		} catch (InterruptedException e) {
			return false;
		}
    }

    /**
     * 初始红包数量
     * @param key
     * @param count
     */
    public void initCount(String key,int count) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        mapCache.putIfAbsent(key,count,3,TimeUnit.DAYS);
    }
    /**
     * 递增
     * @param key
     * @param delta 要增加几(大于0)
     * @return
     */
    public int incr(String key, int delta) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return  mapCache.addAndGet(key, 1);//加1并获取计算后的值
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public int decr(String key, int delta) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return mapCache.addAndGet(key, -delta);//加1并获取计算后的值
    }
}
